/**
 * An asynchronous TCP daytime client
 */

#include <ctime>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
    using namespace std; // For time_t, time and ctime;
    time_t now = time(0);
    return ctime(&now);
}

class tcp_connection
    : public std::enable_shared_from_this<tcp_connection>
{
public:
    typedef std::shared_ptr<tcp_connection> pointer;

    static pointer create(boost::asio::io_context& io_context)
    {
        return pointer(new tcp_connection(io_context));
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        message_ = make_daytime_string();

        boost::asio::async_write(socket_, boost::asio::buffer(message_),
                                 std::bind(&tcp_connection::handle_write, shared_from_this(),
                                           boost::asio::placeholders::error,
                                           boost::asio::placeholders::bytes_transferred));
    }

private:
    // private constructor
    tcp_connection(boost::asio::io_context& io_context)
        : socket_(io_context)
    {
    }

    void handle_write(const boost::system::error_code& /*error*/,
                      size_t bytes_transferred)
    {
        std::cout << message_ << "Write Complete."  << " Writen " << bytes_transferred << " bytes." << std::endl;
    }

    tcp::socket socket_;
    std::string message_;
};

class tcp_server
{
public:
    tcp_server(boost::asio::io_context& io_context)
        : io_context_(io_context),
          acceptor_(io_context, tcp::endpoint(tcp::v4(), 13))
    {
        start_accept();
    }

private:
    void start_accept()
    {
        // 通过为每个连接创建独立的 tcp_connection 对象，并使用智能指针管理，可以在异步操作完成时（如写操作）回调时确保对象仍然存在
        tcp_connection::pointer new_connection = tcp_connection::create(io_context_);

        acceptor_.async_accept(new_connection->socket(),
                               std::bind(&tcp_server::handle_accept, this, new_connection,
                                         boost::asio::placeholders::error
                               ));
    }

    void handle_accept(tcp_connection::pointer new_connection,
                       const boost::system::error_code& error)
    {
        if (!error)
        {
            new_connection->start();
        }

        start_accept();
    }

    /**
    * 在 handle_accept() 中再次调用 start_accept() 是一种常见的方式，用于确保服务器持续监听新连接。这种设计不会导致栈溢出。下面详细解释其中的原因：

    1. 异步调用和栈管理
    handle_accept() 是通过异步机制调用的，而不是递归调用。在异步操作完成时（即有新连接到来时），Boost.Asio 的 I/O 服务会将 handle_accept() 添加到任务队列中，然后在合适的时候执行。
    每次调用 start_accept() 都是将新的异步接受操作放入队列中，而不会立即执行，这与递归调用完全不同。递归调用会不断占用栈空间，而异步操作是基于事件驱动的，调用栈在每次操作之间是独立的。
    2. 事件驱动的异步机制
    在异步编程中，每次异步操作完成时，会触发相应的回调（如 handle_accept()），并不会直接导致函数的再次调用，而是由事件循环（io_context.run()）来调度执行。
    由于 io_context.run() 是一个事件循环，每个异步操作完成后，栈会返回到事件循环，然后再执行下一个任务。这样，栈空间在每次任务之间是释放的，不会累积。
    3. 持续运行的异步服务器
    对于一个长时间运行的异步服务器，它通过不断地调用 start_accept() 来接受新连接，确保服务器持续运行和监听端口。
    这种设计模式非常适合异步 I/O，因为它不会阻塞主线程，也不会导致栈溢出。
    4. 处理并发连接
    每个新连接到来时，都会启动一个新的 tcp_connection 对象，这些对象在处理各自的连接时，彼此之间是独立的，不会影响栈的深度。
    通过这种方式，服务器可以高效地处理多个并发连接，而不会因栈空间的累积而导致问题。
    总结
    在 handle_accept() 中调用 start_accept() 来设置下一个接受操作，不会导致栈溢出。因为这是通过异步事件机制完成的，每个操作之间栈是独立的。
    异步服务器设计的优势就在于可以处理长时间运行和高并发，而不必担心栈空间的问题。
    这种设计模式已经被广泛应用于各种高性能服务器中，是一种非常稳定且高效的异步I/O处理方式。如果你还有其他疑问，请随时告诉我！
     */
    boost::asio::io_context& io_context_;
    tcp::acceptor acceptor_;
};

int main()
{
    try
    {
        boost::asio::io_context io_context;
        tcp_server server(io_context);
        io_context.run();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}
